package com.socket;

import com.alibaba.fastjson.JSON;
import com.config.ProjectConfig;
import com.enums.ProjectStatusEnum;
import com.os.OS;
import com.utils.FileMappingUtil;
import com.utils.ProcessUtil;
import com.utils.SpringContextUtil;
import com.utils.WebSocketJsonData;
import lombok.extern.slf4j.Slf4j;

import javax.websocket.Session;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 公共抽象类
 * @author rkc
 * @date 2022/8/29 15:11
 */
@Slf4j
public abstract class RunnerWebSocket {

    protected Process currentProcess;
    protected Session currentSession;
    protected FileMappingUtil fileMappingUtil = SpringContextUtil.getBean(FileMappingUtil.class);
    protected ProjectConfig projectConfig = SpringContextUtil.getBean(ProjectConfig.class);

    protected String[] cmdArray = new String[3];
    private ThreadPoolExecutor threadPool = new ThreadPoolExecutor(5, 5, 10, TimeUnit.SECONDS,
            new ArrayBlockingQueue<>(10), r -> new Thread(r, "worker"));

    public RunnerWebSocket() {
        if (OS.isWindows()) {
            cmdArray[0] = "cmd";
            cmdArray[1] = "/c";
        } else if (OS.isLinux()) {
            cmdArray[0] = "sh";
            cmdArray[1] = "-c";
        }
    }

    /**
     * 使用jdk9的功能获取process下的所有pid
     * @return
     */
    protected List<Long> getPidList() {
        List<Long> pids = new ArrayList<>();
        List<ProcessHandle> ls = currentProcess.descendants().collect(Collectors.toList());
        for (ProcessHandle handle : ls) {
            pids.add(handle.pid());
        }
        log.info("process的pid={}", pids);
        return pids;
    }

    /**
     * 启动process以及日志打印
     * @param session
     */
    protected void startProcess(Session session) {
        //将输出日志通过websocket推送回前端展示出来
        threadPool.execute(() -> {
            try (BufferedReader reader = new BufferedReader(new InputStreamReader(currentProcess.getInputStream()))) {
                String line;
                while ((line = reader.readLine()) != null) {
                    sendMessage(new WebSocketJsonData(ProjectStatusEnum.LOG_INFO_CONTENT, line, null), session);
                }
            } catch (IOException e) {
                log.error(e.getMessage());
            }
        });

        //打印错误日志
        threadPool.execute(() -> {
            try (BufferedReader reader = new BufferedReader(new InputStreamReader(currentProcess.getErrorStream()))) {
                String line;
                while ((line = reader.readLine()) != null) {
                    sendMessage(new WebSocketJsonData(ProjectStatusEnum.LOG_ERROR_CONTENT, line, null), session);
                }
            } catch (IOException e) {
                log.error(e.getMessage());
            }
        });

        //开一个线程持续检查当前process有没有停止运行
        threadPool.execute(() -> {
            while (currentProcess.isAlive()) {
                try {
                    log.info("process正在运行");
                    sendMessage(new WebSocketJsonData(ProjectStatusEnum.SUCCESSFULLY_STARTED, null, null), session);
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    log.error(e.getMessage());
                }
            }
            log.info("程序停止运行，告诉客户端，{}", currentProcess);
            //停止运行，告知客户端
            sendMessage(new WebSocketJsonData(ProjectStatusEnum.SUCCESSFULLY_STOP, "停止成功", null), session);
        });
    }

    /**
     * 将封装的json对象返回回去
     * @param res
     * @param session
     */
    @SuppressWarnings("SynchronizeOnNonFinalField")
    protected void sendMessage(WebSocketJsonData res, Session session) {
        try {
            if (!session.isOpen()) {
                return;
            }
            synchronized (currentSession) {
                session.getBasicRemote().sendText(JSON.toJSONString(res));
            }
        } catch (IOException e) {
            log.warn(e.getMessage());
        }
    }

    /**
     * 停止process
     * @param pid
     */
    protected void stopProcess(long pid, Session session) {
        log.info("即将kill进程，PID={}", pid);
        //停止成功与否也要告诉客户端
        if (ProcessUtil.killProcessByPid(pid)) {
            sendMessage(new WebSocketJsonData(ProjectStatusEnum.SUCCESSFULLY_STOP, "停止成功", null), session);
        } else {
            sendMessage(new WebSocketJsonData(ProjectStatusEnum.FAILED_STOP, "停止失败", null), session);
        }
    }

    /**
     * kill掉process一系列进程
     * @param pids
     * @param session
     */
    protected void stopProcess(List<Long> pids, Session session) {
        for (Long pid : pids) {
            stopProcess(pid, session);
        }
    }

    /**
     * 将内容写到输出流
     * @param content
     */
    protected void writeText(String content) {
        try (BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(currentProcess.getOutputStream()))) {
            writer.write(content);
            writer.flush();
        } catch (IOException e) {
            log.error(e.getMessage());
        }
    }
}
